package com.salton123.imagepicker;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;

import androidx.core.content.FileProvider;

import com.salton123.imagepicker.log.ILog;

import com.salton123.imagepicker.bean.ImageFolder;
import com.salton123.imagepicker.bean.ImageItem;
import com.salton123.imagepicker.custom.CallBackRepository;
import com.salton123.imagepicker.util.IImageLoader;
import com.salton123.imagepicker.util.ProviderUtil;
import com.salton123.imagepicker.util.Utils;
import com.salton123.imagepicker.view.CropImageView;

import java.io.File;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;

/**
 * ================================================
 * 作    者：jeasonlzy（廖子尧 Github地址：https://github.com/jeasonlzy0216
 * 版    本：1.0
 * 创建日期：2016/5/19
 * 描    述：图片选择的入口类
 * 修订历史：
 * 2017-03-20
 *
 * @author nanchen
 * 采用单例和弱引用解决Intent传值限制导致的异常
 * ================================================
 */
public class ImagePicker {

    public static final String TAG = ImagePicker.class.getSimpleName();
    public static final int REQUEST_CODE_TAKE = 1001;
    public static final int REQUEST_CODE_CROP = 1002;
    public static final int REQUEST_CODE_PREVIEW = 1003;
    public static final int RESULT_CODE_ITEMS = 1004;
    public static final int RESULT_CODE_BACK = 1005;

    public static final String EXTRA_RESULT_ITEMS = "extra_result_items";
    public static final String EXTRA_SELECTED_IMAGE_POSITION = "selected_image_position";
    public static final String EXTRA_IMAGE_ITEMS = "extra_image_items";
    public static final String EXTRA_FROM_ITEMS = "extra_from_items";

    private boolean multiMode = true;    //图片选择模式
    private int selectLimit = 9;         //最大选择图片数量
    private boolean crop = true;         //裁剪
    private boolean showCamera = true;   //显示相机
    private boolean isSaveRectangle = false;  //裁剪后的图片是否是矩形，否者跟随裁剪框的形状
    private int outPutX = 0;           //裁剪保存宽度,如果不指定，则根据缩放系数计算
    private int outPutY = 0;           //裁剪保存高度
    private int focusWidth = 280;         //焦点框的宽度
    private int focusHeight = 280;        //焦点框的高度
    private CropImageView.Style style = CropImageView.Style.RECTANGLE; //裁剪框的形状
    private File cropCacheFolder;
    private File takeImageFile;
    public Bitmap cropBitmap;
    private int selectSizeLimitLower = 0;                              //选择图片大小下限 kb
    private int selectSizeLimitUpper = Integer.MAX_VALUE;              //选择图片大小上限 kb
    private CallBackRepository.ISelectSizeLimitCallback selectSizeLimitCallback;
    private int cropPixelLimit;                             //裁剪图片像素限制,正方形，px
    private CallBackRepository.ICropPixelLimitCallback cropPixelLimitCallback;
    private String cropTextGuide = "";                      //裁剪提示语
    private String cropTextColorGuide = "";
    private CallBackRepository.ISelectAmountLimitCallback selectAmountLimitCallback; //选择图片数量限制回调
    private CallBackRepository.IPermissionRejectCallback permissionRejectCallback;
    private String cropCancelText = "";                     //裁剪取消文案
    private String cropCancelTextColor = "";                //裁剪取消颜色
    private String cropChooseText = "";                     //裁剪确认文案
    private String cropChooseTextColor = "";                //裁剪确认颜色
    private String completeText = "";                       //完成 -- 文案

    private ArrayList<ImageItem> mSelectedImages = new ArrayList<>();   //选中的图片集合
    private ArrayList<ImageFolder> mImageFolders = new ArrayList<>();     //所有的图片文件夹
    private int mCurrentImageFolderPosition = 0;  //当前选中的文件夹位置 0表示所有图片
    private List<OnImageSelectedListener> mImageSelectedListeners;          // 图片选中的监听回调

    private IImageLoader imageLoader; //自定义图片加载器
    private boolean filterGif = true; //是否过滤gif,默认过滤
    private boolean rotateSwitch = false; //是否开启裁剪旋转
    private boolean filterVideo = true; //是否过滤视频，默认过滤
    private long videoSelectSizeLimitUpper = Long.MAX_VALUE; //选择视频大小上限 kb
    private CallBackRepository.IVideoSelectSizeLimitCallback videoSelectSizeLimitCallback; //选择视频上限大小回调
    private CallBackRepository.IClickPreviewListener clickPreviewListener;

    private static ImagePicker mInstance;

    private ImagePicker() {
    }

    public static ImagePicker getInstance() {
        if (mInstance == null) {
            synchronized (ImagePicker.class) {
                if (mInstance == null) {
                    mInstance = new ImagePicker();
                }
            }
        }
        return mInstance;
    }

    public boolean isMultiMode() {
        return multiMode;
    }

    public ImagePicker setMultiMode(boolean multiMode) {
        this.multiMode = multiMode;
        return this;
    }

    public int getSelectLimit() {
        return selectLimit;
    }

    public ImagePicker setSelectLimit(int selectLimit) {
        this.selectLimit = selectLimit;
        return this;
    }

    public boolean isCrop() {
        return crop;
    }

    public ImagePicker setCrop(boolean crop) {
        this.crop = crop;
        return this;
    }

    public boolean isShowCamera() {
        return showCamera;
    }

    public ImagePicker setShowCamera(boolean showCamera) {
        this.showCamera = showCamera;
        return this;
    }

    public boolean isSaveRectangle() {
        return isSaveRectangle;
    }

    public ImagePicker setSaveRectangle(boolean isSaveRectangle) {
        this.isSaveRectangle = isSaveRectangle;
        return this;
    }

    public int getOutPutX() {
        return outPutX;
    }

    public ImagePicker setOutPutX(int outPutX) {
        this.outPutX = outPutX;
        return this;
    }

    public int getOutPutY() {
        return outPutY;
    }

    public ImagePicker setOutPutY(int outPutY) {
        this.outPutY = outPutY;
        return this;
    }

    public int getFocusWidth() {
        return focusWidth;
    }

    public ImagePicker setFocusWidth(int focusWidth) {
        this.focusWidth = focusWidth;
        return this;
    }

    public int getFocusHeight() {
        return focusHeight;
    }

    public ImagePicker setFocusHeight(int focusHeight) {
        this.focusHeight = focusHeight;
        return this;
    }

    public File getTakeImageFile() {
        return takeImageFile;
    }

    public File getCropCacheFolder(Context context) {
        if (cropCacheFolder == null) {
            cropCacheFolder = new File(context.getCacheDir() + "/ImagePicker/cropTemp/");
        }
        return cropCacheFolder;
    }

    public ImagePicker setCropCacheFolder(File cropCacheFolder) {
        this.cropCacheFolder = cropCacheFolder;
        return this;
    }

    public CropImageView.Style getStyle() {
        return style;
    }

    public ImagePicker setStyle(CropImageView.Style style) {
        this.style = style;
        return this;
    }

    public ArrayList<ImageFolder> getImageFolders() {
        return mImageFolders;
    }

    public void addImageFolders(ArrayList<ImageFolder> imageFolders) {
        if (mImageFolders == null) {
            mImageFolders = new ArrayList<>();
        }
        Iterator oldIterator = mImageFolders.iterator();
        Iterator newIterator = imageFolders.iterator();
        while (newIterator.hasNext()) {
            ImageFolder newFolders = (ImageFolder) newIterator.next();
            while (oldIterator.hasNext()) {
                ImageFolder oldFolders = (ImageFolder) oldIterator.next();
                if (newFolders.equals(oldFolders)) {
                    oldFolders.images.addAll(newFolders.images);
                    newIterator.remove();
                }
            }
        }
        //剩下的imageFolders也塞进去
        mImageFolders.addAll(imageFolders);
        for (ImageFolder imageFolder : mImageFolders) {
            Collections.sort(imageFolder.images);
        }
    }

    public int getCurrentImageFolderPosition() {
        return mCurrentImageFolderPosition;
    }

    public ImagePicker setCurrentImageFolderPosition(int mCurrentSelectedImageSetPosition) {
        mCurrentImageFolderPosition = mCurrentSelectedImageSetPosition;
        return this;
    }

    public ArrayList<ImageItem> getCurrentImageFolderItems() {
        return mImageFolders.get(mCurrentImageFolderPosition).images;
    }

    public IImageLoader getImageLoader() {
        return imageLoader;
    }

    public ImagePicker setImageLoader(IImageLoader imageLoader) {
        this.imageLoader = imageLoader;
        return this;
    }

    public boolean isFilterVideo() {
        return filterVideo;
    }

    public ImagePicker setFilterVideo(boolean filterVideo) {
        this.filterVideo = filterVideo;
        return this;
    }

    public long getVideoSelectSizeLimitUpper() {
        return videoSelectSizeLimitUpper;
    }

    public ImagePicker setVideoSelectSizeLimitIntercept(long videoSelectSizeLimitUpper,
                                                        CallBackRepository.IVideoSelectSizeLimitCallback callback) {
        this.videoSelectSizeLimitUpper = videoSelectSizeLimitUpper;
        this.videoSelectSizeLimitCallback = callback;
        return this;
    }

    public CallBackRepository.IVideoSelectSizeLimitCallback getVideoSelectSizeLimitCallback() {
        return videoSelectSizeLimitCallback;
    }

    public boolean isSelect(ImageItem item) {
        return mSelectedImages.contains(item);
    }

    public int getSelectImageCount() {
        if (mSelectedImages == null) {
            return 0;
        }
        return mSelectedImages.size();
    }

    public ArrayList<ImageItem> getSelectedImages() {
        return mSelectedImages;
    }

    public ImagePicker setSelectSizeLimitIntercept(
            int selectSizeLimitLower,
            int selectSizeLimitUpper,
            CallBackRepository.ISelectSizeLimitCallback callback) {
        this.selectSizeLimitLower = selectSizeLimitLower;
        this.selectSizeLimitUpper = selectSizeLimitUpper;
        this.selectSizeLimitCallback = callback;
        return this;
    }

    public ImagePicker setCropPixelLimitIntercept(
            int cropPixelLimit,
            CallBackRepository.ICropPixelLimitCallback callback) {
        this.cropPixelLimit = cropPixelLimit;
        this.cropPixelLimitCallback = callback;
        return this;
    }

    public String getCropTextGuide() {
        return cropTextGuide;
    }

    public String getCropTextColorGuide() {
        return cropTextColorGuide;
    }

    public ImagePicker setCropTextGuide(String cropTextGuide, String cropTextColorGuide) {
        this.cropTextGuide = cropTextGuide;
        this.cropTextColorGuide = cropTextColorGuide;
        return this;
    }

    public CallBackRepository.ISelectAmountLimitCallback getSelectAmountLimitCallback() {
        return selectAmountLimitCallback;
    }

    public ImagePicker setSelectAmountLimitIntercept(
            CallBackRepository.ISelectAmountLimitCallback selectAmountLimitCallback) {
        this.selectAmountLimitCallback = selectAmountLimitCallback;
        return this;
    }

    public String getCropCancelText() {
        return cropCancelText;
    }

    public ImagePicker setCropCancelText(String cropCancelText, String cropCancelTextColor) {
        this.cropCancelText = cropCancelText;
        this.cropCancelTextColor = cropCancelTextColor;
        return this;
    }

    public String getCropCancelTextColor() {
        return cropCancelTextColor;
    }

    public String getCropChooselText() {
        return cropChooseText;
    }

    public ImagePicker setCropChooselText(String cropChooselText, String cropChooseTextColor) {
        this.cropChooseText = cropChooselText;
        this.cropChooseTextColor = cropChooseTextColor;
        return this;
    }

    public String getCropChooseTextColor() {
        return cropChooseTextColor;
    }

    public int getSelectSizeLimitLower() {
        return selectSizeLimitLower;
    }

    public int getSelectSizeLimitUpper() {
        return selectSizeLimitUpper;
    }

    public CallBackRepository.ISelectSizeLimitCallback getSelectSizeLimitCallback() {
        return selectSizeLimitCallback;
    }

    public int getCropPixelLimit() {
        return cropPixelLimit;
    }

    public CallBackRepository.ICropPixelLimitCallback getCropPixelLimitCallback() {
        return cropPixelLimitCallback;
    }

    public boolean isRotateSwitch() {
        return rotateSwitch;
    }

    public ImagePicker setRotateSwitch(boolean rotateSwitch) {
        this.rotateSwitch = rotateSwitch;
        return this;
    }

    public boolean isFilterGif() {
        return filterGif;
    }

    public ImagePicker setFilterGif(boolean filterGif) {
        this.filterGif = filterGif;
        return this;
    }

    public CallBackRepository.IPermissionRejectCallback getPermissionRejectCallback() {
        return permissionRejectCallback;
    }

    public ImagePicker setPermissionRejectCallback(
            CallBackRepository.IPermissionRejectCallback permissionRejectCallback) {
        this.permissionRejectCallback = permissionRejectCallback;
        return this;
    }

    public String getCompleteText() {
        return completeText;
    }

    public ImagePicker setCompleteText(String completeText) {
        this.completeText = completeText;
        return this;
    }

    public CallBackRepository.IClickPreviewListener getClickPreviewListener() {
        return clickPreviewListener;
    }

    public ImagePicker setClickPreviewListener(CallBackRepository.IClickPreviewListener clickPreviewListener) {
        this.clickPreviewListener = clickPreviewListener;
        return this;
    }

    public void clearSelectedImages() {
        if (mSelectedImages != null) mSelectedImages.clear();
    }

    public void clear() {
        if (mImageSelectedListeners != null) {
            mImageSelectedListeners.clear();
            mImageSelectedListeners = null;
        }
        if (mImageFolders != null) {
            mImageFolders.clear();
            mImageFolders = null;
        }
        if (mSelectedImages != null) {
            mSelectedImages.clear();
        }
        mCurrentImageFolderPosition = 0;
    }

    /**
     * 拍照的方法
     */
    public void takePicture(Activity activity, int requestCode) {
        try {
            Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
            takePictureIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
            if (takePictureIntent.resolveActivity(activity.getPackageManager()) != null) {
                if (Utils.existSDCard()) {
                    takeImageFile = new File(Environment.getExternalStorageDirectory(), "/DCIM/camera/");
                } else {
                    takeImageFile = Environment.getDataDirectory();
                }
                takeImageFile = createFile(takeImageFile, "IMG_", ".jpg");
                if (takeImageFile != null) {
                    // 默认情况下，即不需要指定intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
                    // 照相机有自己默认的存储路径，拍摄的照片将返回一个缩略图。如果想访问原始图片，
                    // 可以通过dat extra能够得到原始图片位置。即，如果指定了目标uri，data就没有数据，
                    // 如果没有指定uri，则data就返回有数据！

                    Uri uri;
                    if (VERSION.SDK_INT <= VERSION_CODES.M) {
                        uri = Uri.fromFile(takeImageFile);
                    } else {

                        /**
                         * 7.0 调用系统相机拍照不再允许使用Uri方式，应该替换为FileProvider
                         * 并且这样可以解决MIUI系统上拍照返回size为0的情况
                         */
                        uri = FileProvider.getUriForFile(activity,
                                ProviderUtil.getFileProviderName(activity), takeImageFile);
                        //加入uri权限 要不三星手机不能拍照
                        List<ResolveInfo> resInfoList = activity.getPackageManager().
                                queryIntentActivities(takePictureIntent, PackageManager.MATCH_DEFAULT_ONLY);
                        for (ResolveInfo resolveInfo : resInfoList) {
                            String packageName = resolveInfo.activityInfo.packageName;
                            activity.grantUriPermission(packageName, uri,
                                    Intent.FLAG_GRANT_WRITE_URI_PERMISSION |
                                            Intent.FLAG_GRANT_READ_URI_PERMISSION);
                        }
                    }

                    ILog.e("nanchen", ProviderUtil.getFileProviderName(activity));
                    takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
                }
                activity.startActivityForResult(takePictureIntent, requestCode);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 根据系统时间、前缀、后缀产生一个文件
     */
    public static File createFile(File folder, String prefix, String suffix) {
        if (!folder.exists() || !folder.isDirectory()) folder.mkdirs();
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.CHINA);
        String filename = prefix + dateFormat.format(new Date(System.currentTimeMillis())) + suffix;
        return new File(folder, filename);
    }

    /**
     * 扫描图片
     */
    public static void galleryAddPic(Context context, File file) {
        Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
        Uri contentUri = Uri.fromFile(file);
        mediaScanIntent.setData(contentUri);
        context.sendBroadcast(mediaScanIntent);
    }

    /**
     * 图片选中的监听
     */
    public interface OnImageSelectedListener {
        void onImageSelected(int position, ImageItem item, boolean isAdd);
    }

    public void addOnImageSelectedListener(OnImageSelectedListener l) {
        if (mImageSelectedListeners == null) mImageSelectedListeners = new ArrayList<>();
        mImageSelectedListeners.add(l);
    }

    public void removeOnImageSelectedListener(OnImageSelectedListener l) {
        if (mImageSelectedListeners == null) {
            return;
        }
        if (mImageSelectedListeners.size() > 0) {
            mImageSelectedListeners.remove(l);
        }
    }

    //position 图片的索引位置
    public void addSelectedImageItem(int position, ImageItem item, boolean isAdd) {
        if (isAdd) {
            mSelectedImages.add(item);
        } else {
            if (mSelectedImages.size() > 0) {
                mSelectedImages.remove(item);
            }
        }
        notifyImageSelectedChanged(position, item, isAdd);
    }

    public void setSelectedImages(ArrayList<ImageItem> selectedImages) {
        if (selectedImages == null) {
            return;
        }
        this.mSelectedImages = selectedImages;
    }

    private void notifyImageSelectedChanged(int position, ImageItem item, boolean isAdd) {
        if (mImageSelectedListeners == null) return;
        for (OnImageSelectedListener l : mImageSelectedListeners) {
            l.onImageSelected(position, item, isAdd);
        }
    }

    /**
     * 用于手机内存不足，进程被系统回收，重启时的状态恢复
     */
    public void restoreInstanceState(Bundle savedInstanceState) {
        cropCacheFolder = (File) savedInstanceState.getSerializable("cropCacheFolder");
        takeImageFile = (File) savedInstanceState.getSerializable("takeImageFile");
        style = (CropImageView.Style) savedInstanceState.getSerializable("style");
        multiMode = savedInstanceState.getBoolean("multiMode");
        crop = savedInstanceState.getBoolean("crop");
        showCamera = savedInstanceState.getBoolean("showCamera");
        isSaveRectangle = savedInstanceState.getBoolean("isSaveRectangle");
        selectLimit = savedInstanceState.getInt("selectLimit");
        outPutX = savedInstanceState.getInt("outPutX");
        outPutY = savedInstanceState.getInt("outPutY");
        focusWidth = savedInstanceState.getInt("focusWidth");
        focusHeight = savedInstanceState.getInt("focusHeight");
    }

    /**
     * 用于手机内存不足，进程被系统回收时的状态保存
     */
    public void saveInstanceState(Bundle outState) {
        outState.putSerializable("cropCacheFolder", cropCacheFolder);
        outState.putSerializable("takeImageFile", takeImageFile);
        outState.putSerializable("style", style);
        outState.putBoolean("multiMode", multiMode);
        outState.putBoolean("crop", crop);
        outState.putBoolean("showCamera", showCamera);
        outState.putBoolean("isSaveRectangle", isSaveRectangle);
        outState.putInt("selectLimit", selectLimit);
        outState.putInt("outPutX", outPutX);
        outState.putInt("outPutY", outPutY);
        outState.putInt("focusWidth", focusWidth);
        outState.putInt("focusHeight", focusHeight);
    }

}